use std::os::raw::{c_char, c_int};
use std::ptr;
use std::time::Duration;

use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyModifiers, MouseEvent, MouseEventKind};

use crate::{RatatuiResult, string_to_c_str};

/// Event types
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RatatuiEventType {
    Key = 0,
    Mouse = 1,
    Resize = 2,
    None = 3,
}

/// Key codes
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RatatuiKeyCode {
    Backspace = 0,
    Enter = 1,
    Left = 2,
    Right = 3,
    Up = 4,
    Down = 5,
    Home = 6,
    End = 7,
    PageUp = 8,
    PageDown = 9,
    Tab = 10,
    BackTab = 11,
    Delete = 12,
    Insert = 13,
    F1 = 14,
    F2 = 15,
    F3 = 16,
    F4 = 17,
    F5 = 18,
    F6 = 19,
    F7 = 20,
    F8 = 21,
    F9 = 22,
    F10 = 23,
    F11 = 24,
    F12 = 25,
    Char = 26,
    Esc = 27,
}

/// Key modifiers
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RatatuiKeyModifiers {
    pub shift: bool,
    pub control: bool,
    pub alt: bool,
    pub super_key: bool,
    pub hyper: bool,
    pub meta: bool,
}

/// Event structure
#[repr(C)]
pub struct RatatuiEvent {
    pub event_type: RatatuiEventType,
    pub key_code: RatatuiKeyCode,
    pub key_char: c_char,
    pub key_modifiers: RatatuiKeyModifiers,
    pub mouse_x: u16,
    pub mouse_y: u16,
    pub resize_width: u16,
    pub resize_height: u16,
}

impl RatatuiEvent {
    fn new() -> Self {
        RatatuiEvent {
            event_type: RatatuiEventType::None,
            key_code: RatatuiKeyCode::Char,
            key_char: 0,
            key_modifiers: RatatuiKeyModifiers {
                shift: false,
                control: false,
                alt: false,
                super_key: false,
                hyper: false,
                meta: false,
            },
            mouse_x: 0,
            mouse_y: 0,
            resize_width: 0,
            resize_height: 0,
        }
    }

    fn from_crossterm_event(event: Event) -> Self {
        let mut ratatui_event = RatatuiEvent::new();

        match event {
            Event::Key(key_event) => {
                ratatui_event.event_type = RatatuiEventType::Key;
                ratatui_event.key_code = match key_event.code {
                    KeyCode::Backspace => RatatuiKeyCode::Backspace,
                    KeyCode::Enter => RatatuiKeyCode::Enter,
                    KeyCode::Left => RatatuiKeyCode::Left,
                    KeyCode::Right => RatatuiKeyCode::Right,
                    KeyCode::Up => RatatuiKeyCode::Up,
                    KeyCode::Down => RatatuiKeyCode::Down,
                    KeyCode::Home => RatatuiKeyCode::Home,
                    KeyCode::End => RatatuiKeyCode::End,
                    KeyCode::PageUp => RatatuiKeyCode::PageUp,
                    KeyCode::PageDown => RatatuiKeyCode::PageDown,
                    KeyCode::Tab => RatatuiKeyCode::Tab,
                    KeyCode::BackTab => RatatuiKeyCode::BackTab,
                    KeyCode::Delete => RatatuiKeyCode::Delete,
                    KeyCode::Insert => RatatuiKeyCode::Insert,
                    KeyCode::F(1) => RatatuiKeyCode::F1,
                    KeyCode::F(2) => RatatuiKeyCode::F2,
                    KeyCode::F(3) => RatatuiKeyCode::F3,
                    KeyCode::F(4) => RatatuiKeyCode::F4,
                    KeyCode::F(5) => RatatuiKeyCode::F5,
                    KeyCode::F(6) => RatatuiKeyCode::F6,
                    KeyCode::F(7) => RatatuiKeyCode::F7,
                    KeyCode::F(8) => RatatuiKeyCode::F8,
                    KeyCode::F(9) => RatatuiKeyCode::F9,
                    KeyCode::F(10) => RatatuiKeyCode::F10,
                    KeyCode::F(11) => RatatuiKeyCode::F11,
                    KeyCode::F(12) => RatatuiKeyCode::F12,
                    KeyCode::Char(c) => {
                        ratatui_event.key_char = c as c_char;
                        RatatuiKeyCode::Char
                    }
                    KeyCode::Esc => RatatuiKeyCode::Esc,
                    _ => RatatuiKeyCode::Char,
                };

                ratatui_event.key_modifiers = RatatuiKeyModifiers {
                    shift: key_event.modifiers.contains(KeyModifiers::SHIFT),
                    control: key_event.modifiers.contains(KeyModifiers::CONTROL),
                    alt: key_event.modifiers.contains(KeyModifiers::ALT),
                    super_key: key_event.modifiers.contains(KeyModifiers::SUPER),
                    hyper: key_event.modifiers.contains(KeyModifiers::HYPER),
                    meta: key_event.modifiers.contains(KeyModifiers::META),
                };
            }
            Event::Mouse(mouse_event) => {
                ratatui_event.event_type = RatatuiEventType::Mouse;
                ratatui_event.mouse_x = mouse_event.column;
                ratatui_event.mouse_y = mouse_event.row;

                // Store mouse event details in key_modifiers for now
                ratatui_event.key_modifiers = RatatuiKeyModifiers {
                    shift: mouse_event.modifiers.contains(KeyModifiers::SHIFT),
                    control: mouse_event.modifiers.contains(KeyModifiers::CONTROL),
                    alt: mouse_event.modifiers.contains(KeyModifiers::ALT),
                    super_key: mouse_event.modifiers.contains(KeyModifiers::SUPER),
                    hyper: mouse_event.modifiers.contains(KeyModifiers::HYPER),
                    meta: mouse_event.modifiers.contains(KeyModifiers::META),
                };
            }
            Event::Resize(width, height) => {
                ratatui_event.event_type = RatatuiEventType::Resize;
                ratatui_event.resize_width = width;
                ratatui_event.resize_height = height;
            }
            _ => {
                ratatui_event.event_type = RatatuiEventType::None;
            }
        }

        ratatui_event
    }
}

/// Poll for events with timeout
#[no_mangle]
pub extern "C" fn ratatui_poll_event(
    event: *mut *mut RatatuiEvent,
    timeout_ms: c_int,
) -> c_int {
    if event.is_null() {
        return RatatuiResult::ErrorNullPointer as c_int;
    }

    let timeout = if timeout_ms < 0 {
        None
    } else {
        Some(Duration::from_millis(timeout_ms as u64))
    };

    match if let Some(timeout) = timeout {
        event::poll(timeout)
    } else {
        Ok(true) // Block indefinitely
    } {
        Ok(true) => {
            match event::read() {
                Ok(crossterm_event) => {
                    let ratatui_event = RatatuiEvent::from_crossterm_event(crossterm_event);
                    unsafe {
                        *event = Box::into_raw(Box::new(ratatui_event));
                    }
                    1 // Event available
                }
                Err(_) => RatatuiResult::ErrorEventPoll as c_int,
            }
        }
        Ok(false) => 0, // No event available (timeout)
        Err(_) => RatatuiResult::ErrorEventPoll as c_int,
    }
}

/// Free an event
#[no_mangle]
pub extern "C" fn ratatui_event_free(event: *mut RatatuiEvent) {
    if !event.is_null() {
        unsafe {
            let _ = Box::from_raw(event);
        }
    }
}

/// Check if event is a key event
#[no_mangle]
pub extern "C" fn ratatui_event_is_key(event: *const RatatuiEvent) -> bool {
    if event.is_null() {
        return false;
    }
    unsafe {
        let event = &*event;
        event.event_type == RatatuiEventType::Key
    }
}

/// Check if event is a quit key (Ctrl+C or 'q')
#[no_mangle]
pub extern "C" fn ratatui_event_is_quit(event: *const RatatuiEvent) -> bool {
    if event.is_null() {
        return false;
    }
    unsafe {
        let event = &*event;
        if event.event_type == RatatuiEventType::Key {
            // Check for Ctrl+C
            if event.key_modifiers.control && event.key_char == b'c' as c_char {
                return true;
            }
            // Check for 'q' key
            if event.key_code == RatatuiKeyCode::Char && event.key_char == b'q' as c_char {
                return true;
            }
        }
        false
    }
}

/// Mouse event types
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RatatuiMouseEventType {
    Down = 0,
    Up = 1,
    Drag = 2,
    Moved = 3,
    ScrollDown = 4,
    ScrollUp = 5,
}

/// Mouse button types
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RatatuiMouseButton {
    Left = 0,
    Right = 1,
    Middle = 2,
}

/// Extended mouse event structure
#[repr(C)]
pub struct RatatuiMouseEvent {
    pub event_type: RatatuiMouseEventType,
    pub button: RatatuiMouseButton,
    pub x: u16,
    pub y: u16,
    pub modifiers: RatatuiKeyModifiers,
}

/// Get mouse event details
#[no_mangle]
pub extern "C" fn ratatui_event_get_mouse(
    event: *const RatatuiEvent,
    mouse_event: *mut RatatuiMouseEvent,
) -> c_int {
    if event.is_null() || mouse_event.is_null() {
        return RatatuiResult::ErrorNullPointer as c_int;
    }

    unsafe {
        let event = &*event;
        if event.event_type != RatatuiEventType::Mouse {
            return RatatuiResult::ErrorInvalidArgument as c_int;
        }

        let mouse = &mut *mouse_event;
        mouse.x = event.mouse_x;
        mouse.y = event.mouse_y;
        mouse.modifiers = event.key_modifiers;

        // Default values (would need more detailed crossterm event info)
        mouse.event_type = RatatuiMouseEventType::Down;
        mouse.button = RatatuiMouseButton::Left;

        RatatuiResult::Ok as c_int
    }
}

/// Get key event details
#[no_mangle]
pub extern "C" fn ratatui_event_get_key_char(event: *const RatatuiEvent) -> c_char {
    if event.is_null() {
        return 0;
    }
    unsafe {
        let event = &*event;
        if event.event_type == RatatuiEventType::Key {
            event.key_char
        } else {
            0
        }
    }
}

/// Get key code
#[no_mangle]
pub extern "C" fn ratatui_event_get_key_code(event: *const RatatuiEvent) -> RatatuiKeyCode {
    if event.is_null() {
        return RatatuiKeyCode::Char;
    }
    unsafe {
        let event = &*event;
        if event.event_type == RatatuiEventType::Key {
            event.key_code
        } else {
            RatatuiKeyCode::Char
        }
    }
}

/// Check if key has modifier
#[no_mangle]
pub extern "C" fn ratatui_event_has_modifier(
    event: *const RatatuiEvent,
    modifier: c_int,
) -> bool {
    if event.is_null() {
        return false;
    }
    unsafe {
        let event = &*event;
        if event.event_type == RatatuiEventType::Key {
            match modifier {
                0 => event.key_modifiers.shift,
                1 => event.key_modifiers.control,
                2 => event.key_modifiers.alt,
                3 => event.key_modifiers.super_key,
                4 => event.key_modifiers.hyper,
                5 => event.key_modifiers.meta,
                _ => false,
            }
        } else {
            false
        }
    }
}

/// Get resize event dimensions
#[no_mangle]
pub extern "C" fn ratatui_event_get_resize(
    event: *const RatatuiEvent,
    width: *mut u16,
    height: *mut u16,
) -> c_int {
    if event.is_null() || width.is_null() || height.is_null() {
        return RatatuiResult::ErrorNullPointer as c_int;
    }

    unsafe {
        let event = &*event;
        if event.event_type == RatatuiEventType::Resize {
            *width = event.resize_width;
            *height = event.resize_height;
            RatatuiResult::Ok as c_int
        } else {
            RatatuiResult::ErrorInvalidArgument as c_int
        }
    }
}

/// Event callback function type
pub type RatatuiEventCallback = extern "C" fn(*const RatatuiEvent, *mut std::os::raw::c_void);

/// Event handler structure
pub struct RatatuiEventHandler {
    pub callback: Option<RatatuiEventCallback>,
    pub user_data: *mut std::os::raw::c_void,
}

impl RatatuiEventHandler {
    pub fn new() -> Self {
        RatatuiEventHandler {
            callback: None,
            user_data: ptr::null_mut(),
        }
    }
}

/// Create event handler
#[no_mangle]
pub extern "C" fn ratatui_event_handler_new() -> *mut RatatuiEventHandler {
    let handler = RatatuiEventHandler::new();
    Box::into_raw(Box::new(handler))
}

/// Free event handler
#[no_mangle]
pub extern "C" fn ratatui_event_handler_free(handler: *mut RatatuiEventHandler) {
    if !handler.is_null() {
        unsafe {
            let _ = Box::from_raw(handler);
        }
    }
}

/// Set event callback
#[no_mangle]
pub extern "C" fn ratatui_event_handler_set_callback(
    handler: *mut RatatuiEventHandler,
    callback: RatatuiEventCallback,
    user_data: *mut std::os::raw::c_void,
) -> c_int {
    if handler.is_null() {
        return RatatuiResult::ErrorNullPointer as c_int;
    }

    unsafe {
        let handler = &mut *handler;
        handler.callback = Some(callback);
        handler.user_data = user_data;
        RatatuiResult::Ok as c_int
    }
}

/// Process event with handler
#[no_mangle]
pub extern "C" fn ratatui_event_handler_process(
    handler: *const RatatuiEventHandler,
    event: *const RatatuiEvent,
) -> c_int {
    if handler.is_null() || event.is_null() {
        return RatatuiResult::ErrorNullPointer as c_int;
    }

    unsafe {
        let handler = &*handler;
        if let Some(callback) = handler.callback {
            callback(event, handler.user_data);
            RatatuiResult::Ok as c_int
        } else {
            RatatuiResult::ErrorInvalidArgument as c_int
        }
    }
}

/// Check if event is a mouse event
#[no_mangle]
pub extern "C" fn ratatui_event_is_mouse(event: *const RatatuiEvent) -> bool {
    if event.is_null() {
        return false;
    }
    unsafe {
        let event = &*event;
        event.event_type == RatatuiEventType::Mouse
    }
}

/// Get mouse position
#[no_mangle]
pub extern "C" fn ratatui_event_get_mouse_pos(
    event: *const RatatuiEvent,
    x: *mut u16,
    y: *mut u16,
) -> c_int {
    if event.is_null() || x.is_null() || y.is_null() {
        return RatatuiResult::ErrorNullPointer as c_int;
    }

    unsafe {
        let event = &*event;
        if event.event_type == RatatuiEventType::Mouse {
            *x = event.mouse_x;
            *y = event.mouse_y;
            RatatuiResult::Ok as c_int
        } else {
            RatatuiResult::ErrorInvalidArgument as c_int
        }
    }
}

/// Check if event is a resize event
#[no_mangle]
pub extern "C" fn ratatui_event_is_resize(event: *const RatatuiEvent) -> bool {
    if event.is_null() {
        return false;
    }
    unsafe {
        let event = &*event;
        event.event_type == RatatuiEventType::Resize
    }
}
